
#include "McElementRebarExpression.h"

#include <codecvt>
#include "exprtk.hpp"

/////////////////////////////////////////////////////////////////////////////////////////
// McElementRebarExpressionImp
/////////////////////////////////////////////////////////////////////////////////////////

class McElementRebarExpressionImp
{
protected:
	typedef exprtk::symbol_table<double> symbol_table_t;
	typedef exprtk::expression<double>   expression_t;
	typedef exprtk::parser<double>       parser_t;

	symbol_table_t m_symbol_table;

public:
	McElementRebarExpressionImp()
	{

	}

	std::string to_utf8(const std::wstring& src)
	{
		std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
		return converter.to_bytes(src);
	}

	void setRebarVars(const McRebarExpressionVar* global, const McRebarExpressionVar* local)
	{
		m_symbol_table.clear();

		if (global != nullptr)
		{
			for (auto it = global->vars.begin(); it != global->vars.end(); ++it)
			{
				double& value = const_cast<double&>(it->second);
				m_symbol_table.add_variable(to_utf8(it->first), value);
			}
		}

		if (local != nullptr)
		{
			for (auto it = local->vars.begin(); it != local->vars.end(); ++it)
			{
				double& value = const_cast<double&>(it->second);
				m_symbol_table.add_variable(to_utf8(it->first), value);
			}
		}

		m_symbol_table.add_constants();
	}

	double calc(const std::wstring& expr)
	{
		expression_t expression;
		expression.register_symbol_table(m_symbol_table);

		parser_t parser;
		parser.compile(to_utf8(expr), expression);

		return expression.value();
	}
};

/////////////////////////////////////////////////////////////////////////////////////////
// McElementRebarExpression
/////////////////////////////////////////////////////////////////////////////////////////

McElementRebarExpression::McElementRebarExpression()
	:m_imp(new McElementRebarExpressionImp)
{
}

McElementRebarExpression::~McElementRebarExpression()
{
	if (m_imp != nullptr)
	{
		delete m_imp;
		m_imp = nullptr;
	}
}

void McElementRebarExpression::setRebarVars(const McRebarExpressionVar* global, const McRebarExpressionVar* local)
{
	return m_imp->setRebarVars(global, local);
}

double McElementRebarExpression::calc(const std::wstring& expr)
{
	return m_imp->calc(expr);
}
